package cn.sola.rank;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;

import cn.sola.util.DateUtil;


/**
 * @author: kangxiaohui
 * @date：2018年8月17日 下午4:09:38
 * @version 1.0 缓存 答题活动
 */
public class RedisUtil {

	private static final Logger log = LoggerFactory.getLogger(RedisUtil.class);

	private static final String base = "user:score";// key 的前缀
	
	public static final Long week_second = 7 * 24 * 60 * 60L;//默认过期时间为一周

	public static final String CateAnswerActivity = base + ":" + "CateAnswerActivity";// 活动key
	
	public static final String day_question_quantity = base + ":" + "DayQuestionQuantity"  + ":activityId_";// 每日答题数量

	private RedisTemplate<Serializable, Object> redisTemplate;
	
	/**
	 * 获取有序集合
	 * @return
	 */
	public ZSetOperations<Serializable, Object> getZSetOperations(){
		return redisTemplate.opsForZSet();
		
	}
	
	/**
	 * 增加答题记录 答对题的数量进行缓存
	 * @param userId  用户id
	 * @param yeeQuantity 对的题目数量
	 */
	public void addUserDayQuestionQuantity(Long userId, Integer yesQuantity, Integer activityId, Date answerDate){
		String key  = day_question_quantity + activityId;
		String date = DateUtil.dateToString(answerDate, "yyyyMMdd");
		//排行分数
		long storageScore = yesQuantity * 10000000000000L + (9999999999999L - System.currentTimeMillis());
		//真实答题数量
		getZSetOperations().incrementScore(key + ":" + date , userId, yesQuantity);
		//分数排行 保证答对题目相同的情况下,先达到要求的排在前面
		getZSetOperations().incrementScore(key + ":score:" + date , userId, storageScore);
	}
	
	/**
	 * 合并本周的数据
	 * @param date 当前时间到周一的时间 合并用户答题的数据
	 */
	public void countUserAnswerQuestionQuantutyWeek(Date date, Integer activityId){
		String key  = day_question_quantity + activityId;
		List<String> week = DateUtil.getWeekStartDateToNow(date);
		List<Serializable> keys = new ArrayList<>();
		List<Serializable> scoreKeys = new ArrayList<>();
		for(int index = 0 ; index < week.size(); index ++){
			keys.add(key + ":" + week.get(index));
			scoreKeys.add(key + ":score:" + week.get(index));
		}
		//答题数据
		getZSetOperations().unionAndStore("1", keys, getWeekKey(date,activityId).get("weekKey"));
		//答题排行
		getZSetOperations().unionAndStore("1", scoreKeys, getWeekKey(date,activityId).get("weekScoreKey"));
	}
	
	public Map<String, String> getWeekKey(Date date, Integer activityId){
		String key  = day_question_quantity + activityId;
		//得到周一的日期
		Date monday = DateUtil.getWeekStartDate(date);
		//得到周日
		Date sunday = DateUtil.getAfterDate(6,monday);
		String weekKey  = key + ":" + "week:" + DateUtil.dateToString(monday, "yyyy-MM-dd") + "~" + DateUtil.dateToString(sunday, "yyyy-MM-dd");
		String weekScoreKey  = key + ":week:score:" + DateUtil.dateToString(monday, "yyyy-MM-dd") + "~" + DateUtil.dateToString(sunday, "yyyy-MM-dd");
		Map<String, String> map = new HashMap<>();
		map.put("weekKey", weekKey);
		map.put("weekScoreKey", weekScoreKey);
		return map;
	}
	
	/**
	 * 答题排行
	 * 获取排行的userIds 倒叙排列
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<Object> getWeekRank(long start, long end, Integer activityId){
		return getZSetOperations().reverseRange(getWeekKey(DateUtil.getNowDate(),activityId).get("weekScoreKey"), start, end);
	}
	
	/**
	 * 答题排行 上一周
	 * 获取排行的userIds 倒叙排列
	 * @param start
	 * @param end
	 * @return
	 */
	public Set<Object> getLastWeekRank(long start, long end, Integer activityId){
		return getZSetOperations().reverseRange(getWeekKey(DateUtil.getLastSunday(),activityId).get("weekScoreKey"), start, end);
	}
	
	/**
	  * 答题数据
	 * 获取排行的userIds 倒叙排列
	 * @param start
	 * @param end
	 * @param activityId
	 * @return
	 */
	public Set<ZSetOperations.TypedTuple<Object>> getWeekUserRealAnswerQuantity(long start, long end, Integer activityId){
		Set<ZSetOperations.TypedTuple<Object>> tuples  = getZSetOperations().reverseRangeWithScores(getWeekKey(DateUtil.getNowDate(),activityId).get("weekKey"), start, end);
		return tuples;
	}
	
	/**
	 * 答题数据 上一周
	 * 获取排行的userIds 倒叙排列
	 * @param start
	 * @param end
	 * @param activityId
	 * @return
	 */
	public Set<ZSetOperations.TypedTuple<Object>> getLastWeekUserRealAnswerQuantity(long start, long end, Integer activityId){
		Set<ZSetOperations.TypedTuple<Object>> tuples  = getZSetOperations().reverseRangeWithScores(getWeekKey(DateUtil.getLastSunday(),activityId).get("weekKey"), start, end);
		return tuples;
	}
	
	
	
	/**
	 * 批量删除对应的value
	 * 
	 * @param keys
	 */
	public void remove(final String... keys) {
		for (String key : keys) {
			remove(key);
		}
	}

	/**
	 * 批量删除key
	 * 
	 * @param pattern
	 */
	public void removePattern(final String pattern) {
		Set<Serializable> keys = redisTemplate.keys(pattern);
		if (keys.size() > 0)
			redisTemplate.delete(keys);
	}

	/**
	 * 删除对应的value
	 * 
	 * @param key
	 */
	public void remove(final String key) {
		if (exists(key)) {
			redisTemplate.delete(key);
		}
	}

	/**
	 * 判断缓存中是否有对应的value
	 * 
	 * @param key
	 * @return
	 */
	public boolean exists(final String key) {
		return redisTemplate.hasKey(key);
	}

	/**
	 * 读取缓存
	 * 
	 * @param key
	 * @return
	 */
	public Object get(final String key) {
		Object result = null;
		ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
		result = operations.get(key);
		return result;
	}

	/**
	 * 写入缓存
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean set(final String key, Object value) {
		boolean result = false;
		try {
			ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
			operations.set(key, value);
			result = true;
		} catch (Exception e) {
			log.error("set cache error", e);
		}
		return result;
	}

	/**
	 * 写入缓存
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	public boolean set(final String key, Object value, Long expireTime) {
		boolean result = false;
		try {
			ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
			operations.set(key, value);
			redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
			result = true;
		} catch (Exception e) {
			log.error("set cache error", e);
		}
		return result;
	}

	public long increment(final String key, long delta) {
		return redisTemplate.opsForValue().increment(key, delta);
	}

	public void setRedisTemplate(RedisTemplate<Serializable, Object> redisTemplate) {
		this.redisTemplate = redisTemplate;
	}
}
